### 实现加密算法
import random
import json

class Encrpt:
    # 明文是16比特整数类型
    def __init__(self):
        self.SboxTable = [0xe, 0x4, 0xD, 0x1,
                          0x2, 0xF, 0xB, 0x8,
                          0x3, 0xA, 0x6, 0xC,
                          0x5, 0x9, 0x0, 0x7]
        self.invSboxTable = [0xe, 0x3, 0x4, 0x8,
                             0x1, 0xc, 0xa, 0xf,
                             0x7, 0xd, 0x9, 0x6,
                             0xb, 0x2, 0x0, 0x5]
        self.PTable = [0, 4, 8, 12, 1, 5, 9, 13, 2, 6, 10, 14, 3, 7, 11, 15]
        # self.rk = [0x675b, 0x765a, 0xeca1, 0x6be5, 0xc57e]  # 轮密钥
        self.rk = [0x32ab, 0x684d, 0xaa93, 0x4800, 0x463c]


    def sbox(self, x):
        input_list = [((x >> (3 - i) * 4) & 0xf) for i in range(4)]
        output_list = []
        for i in input_list:
            output_list.append(self.SboxTable[i])
        afterSbox = list_to_int(output_list, 4)
        return afterSbox

    def sbox_inv(self, x):
        input_list = [((x >> (3 - i) * 4) & 0xf) for i in range(4)]
        output_list = []
        for i in input_list:
            output_list.append(self.invSboxTable[i])
        afterSbox = list_to_int(output_list, 4)
        return afterSbox

    def P(self, x):
        # 输入为16比特大小的int类型
        x_list = int_to_list(x, 16)
        Px_list = []
        for i in range(16):
            Px_list.append(x_list[self.PTable[i]])
        return list_to_int(Px_list, 1)

    def P_inv(self,x):
        x_list = int_to_list(x, 16)
        Px_list = [0 for _ in range(16)]
        for i in range(16):
            ind = self.PTable[i]
            Px_list[ind] = x_list[i]
        return list_to_int(Px_list, 1)

    def mixKey(self, x, i):
        # 第i轮轮密钥加
        return x ^ self.rk[i]

    def enc(self, plaintext):
        x = plaintext
        for i in range(3):
            x = self.mixKey(x, i)
            x = self.sbox(x)
            x = self.P(x)
        x = self.mixKey(x, 3)
        x = self.sbox(x)
        cipher = self.mixKey(x, 4)
        return cipher

    def dec(self,ciphertext):
        x = self.mixKey(ciphertext,4)
        for i in range(3):
            x = self.sbox_inv(x)
            x = self.mixKey(x,4 - 1 - i)
            x = self.P_inv(x)
        x = self.sbox_inv(x)
        x = self.mixKey(x,0)
        return x



def int_to_list(x, n):
    # n比特整数到大小为n的list,高比特在list最左边
    ret = [0 for _ in range(n)]
    for i in range(n):
        ret[n - 1 - i] = x & 1
        x >>= 1
    return ret


def list_to_int(l, n):
    # l表示列表，n表示列表中每个元素的比特位数
    ret = 0
    for li in l:
        ret <<= n
        ret += li
    return ret


if __name__ == "__main__":
    # 生成10000轮明密文对
    PCpairs = []
    SPNenc = Encrpt()
    for i in range(10000):
        plaintext = random.randint(0, (1 << 16) - 1)
        ciphertext = SPNenc.enc(plaintext)
        PCpairs.append((plaintext, ciphertext))
    with open('Plain_Cipher_pairs.json', 'w') as fp:
        json.dump({
            'PC_pairs': PCpairs,
        }, fp)

